﻿
// Originally from: http://music.columbia.edu/pipermail/music-dsp/2001-October/045433.html

// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain

using DefaultDspPluginsLib;
using SoundEngine3;
using SoundEngine3.AudioProcessing;
using SoundEngine3.AudioProcessing.Algorithms;
using SoundEngine3.DspPlugins;
using SoundEngine3.DspPlugins.Meta;
using SoundEngine3.MIDI.Meta;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using ZquenceStudio3.Core;
using ZquenceStudio3.Core.DspPlugins;
using ZquenceStudio3.DspPlugins;

namespace ZquenceStudio.DefaultDspPlugins.Effects
{
    public class ReverbDescription : IDspPlugin
    {
        public DspPluginInfo GetDspPluginInfo()
        {
            return new DspPluginInfo("Reverb", DspType.Effect);
        }

        public BaseDspPluginInstance CreateDsp(ZquenceStudio3Context context, DspPluginInfo info)
        {
            return new Reverb(context, info);
        }
    }

    public class Reverb : CoreDspEffectPlugin
    {
        private IntPtr mReverbPtr;
        private DefaultDspPluginsLib.Depricated.Effect_Reverb mReverbCs;

        const double scalewet = 3;
        const double initialroom = 0.83;
        const double initialdamp = 0.69;
        const double initialwet = 1 / scalewet;
        const double initialdry = 0.86;
        const double initialwidth = 1;
        const double initialmode = 0;

        private double mRoomsSize;
        private double mDamp;
        private double mWet;
        private double mDry;
        private double mWidth;
        private int mMode;

        public Reverb(ZquenceStudio3Context soundEngineContext, DspPluginInfo info)
            : base(soundEngineContext, info)
        {
#if !Portable
            mReverbPtr = Effect_Reverb_Create();
#endif
            mReverbCs = new DefaultDspPluginsLib.Depricated.Effect_Reverb();

            mWet = (initialwet);
            mRoomsSize = (initialroom);
            mDry = (initialdry);
            mDamp = (initialdamp);
            mWidth = (initialwidth);
            mMode = (int)(initialmode);
            Update();

            CreateParameters();

            SupportAudioProcessing = true;
        }

        ~Reverb()
        {
#if !Portable
            Effect_Reverb_Delete(mReverbPtr);
#endif
            mReverbCs = null;
        }

        private void CreateParameters()
        {
            // Wet.
            ParameterPropertiesDirect wet = new ParameterPropertiesDirect(this);
            wet.Label = "Wet";
            wet.DirectGet += delegate() { return Wet; };
            wet.DirectSet += delegate(double value) { Wet = value; };
            wet.MinValue = 0;
            wet.MaxValue = 1;
            AddParameter(wet);

            // Dry.
            ParameterPropertiesDirect dry = new ParameterPropertiesDirect(this);
            dry.Label = "Dry";
            dry.DirectGet += delegate() { return Dry; };
            dry.DirectSet += delegate(double value) { Dry = value; };
            dry.MinValue = 0;
            dry.MaxValue = 1;
            AddParameter(dry);

            // Room size.
            ParameterPropertiesDirect roomSize = new ParameterPropertiesDirect(this);
            roomSize.Label = "Room size";
            roomSize.DirectGet += delegate() { return RoomsSize; };
            roomSize.DirectSet += delegate(double value) { RoomsSize = value; };
            roomSize.MinValue = 0;
            roomSize.MaxValue = 1;
            AddParameter(roomSize);

            // Damp.
            ParameterPropertiesDirect damp = new ParameterPropertiesDirect(this);
            damp.Label = "Damp";
            damp.DirectGet += delegate() { return Damp; };
            damp.DirectSet += delegate(double value) { Damp = value; };
            damp.MinValue = 0;
            damp.MaxValue = 1;
            AddParameter(damp);

            // Width.
            ParameterPropertiesDirect width = new ParameterPropertiesDirect(this);
            width.Label = "Width";
            width.DirectGet += delegate() { return Width; };
            width.DirectSet += delegate(double value) { Width = value; };
            width.MinValue = 0;
            width.MaxValue = 1;
            AddParameter(width);

            // Mode.
            ParameterPropertiesDirect mode = new ParameterPropertiesDirect(this);
            mode.Label = "Mode";
            mode.DirectGet += delegate() { return (int)Mode; };
            mode.DirectSet += delegate(double value) { Mode = (int)value; };
            mode.MinValue = 0;
            mode.MaxValue = 1;
            mode.IsInteger = true;
            AddParameter(mode);
        }

        public double RoomsSize
        {
            get { return mRoomsSize; }
            set
            {
                mRoomsSize = value;
                Update();
            }
        }

        public double Damp
        {
            get { return mDamp; }
            set
            {
                mDamp = value;
                Update();
            }
        }

        public double Wet
        {
            get { return mWet; }
            set
            {
                mWet = value;
                Update();
            }
        }

        public double Dry
        {
            get { return mDry; }
            set
            {
                mDry = value;
                Update();
            }
        }

        public double Width
        {
            get { return mWidth; }
            set
            {
                mWidth = value;
                Update();
            }
        }

        public int Mode
        {
            get { return mMode; }
            set
            {
                if (value < 0 || value > 1)
                {
                    throw new IndexOutOfRangeException("Mode can only be 0 or 1.");
                }
                mMode = value;
                Update();
            }
        }

        void Update()
        {
#if !Portable
            Effect_Reverb_Setup(mReverbPtr, mRoomsSize, mDamp, mWet, mDry, mWidth, mMode);
#endif
            mReverbCs.Setup(mRoomsSize, mDamp, mWet, mDry, mWidth, mMode);
        }

        public struct ReverbData
        {
            public int channels;
            public int Length;
        }

#if !Portable
        [DllImport(NativeSoundEngineDllImport.Dll)]
        public static extern IntPtr Effect_Reverb_Create();

        [DllImport(NativeSoundEngineDllImport.Dll)]
        public static extern void Effect_Reverb_Delete(IntPtr ptr);

        [DllImport(NativeSoundEngineDllImport.Dll)]
        public static extern void Effect_Reverb_Setup(IntPtr ptr, double roomsize, double damp, double wet, double dry, double width, int mode);

        [DllImport(NativeSoundEngineDllImport.Dll)]
        public static extern void Effect_Reverb_Clear(IntPtr ptr);

        [DllImport(NativeSoundEngineDllImport.Dll)]
        public static extern void Effect_Reverb_Process(IntPtr reverbPtr, ref ReverbData data,
            ref double wav_in_left, ref double wav_in_right, ref double wav_out_left, ref double wav_out_right);
#endif

        public override void Process(BaseAudioBuffer input_, BaseAudioBuffer output)
        {
            double[] AudioBufferLeft = input_.Channels[0].Samples;
            double[] AudioBufferRight = input_.Channels[1].Samples;
            double[] AudioBufferLeft_ouput = output.Channels[0].Samples;
            double[] AudioBufferRight_output = output.Channels[1].Samples;
            
            ReverbData data;
            data.channels = SoundEngineContext.OutputChannels;
            data.Length = AudioBufferLeft.Length;

            if (NativeSoundEngineDllImport.UseBenchmarking)
            {
                ZquenceStudio3.Core.Utils.ProfilerMeter.Start1("Reverb");
#if !Portable
                Effect_Reverb_Process(mReverbPtr, ref data, ref AudioBufferLeft[0], ref AudioBufferRight[0], ref AudioBufferLeft_ouput[0], ref AudioBufferRight_output[0]);
#endif
                ZquenceStudio3.Core.Utils.ProfilerMeter.Stop1("Reverb");
                ZquenceStudio3.Core.Utils.ProfilerMeter.Start2("Reverb");
                mReverbCs.Process(ref data, AudioBufferLeft, AudioBufferRight, AudioBufferLeft_ouput, AudioBufferRight_output);
                ZquenceStudio3.Core.Utils.ProfilerMeter.Stop2("Reverb");
            }
            else
            {
                if (NativeSoundEngineDllImport.UseNativeProcessing)
                {
#if !Portable
                    Effect_Reverb_Process(mReverbPtr, ref data, ref AudioBufferLeft[0], ref AudioBufferRight[0], ref AudioBufferLeft_ouput[0], ref AudioBufferRight_output[0]);
#endif
                }
                else
                {
                    mReverbCs.Process(ref data, AudioBufferLeft, AudioBufferRight, AudioBufferLeft_ouput, AudioBufferRight_output);
                }
            }
        }

        public override Version Version
        {
            get { return DspPluginInfo.DefaultVersion; }
        }

        public override string PluginInfo
        {
            get { return DspPluginInfo.DefaultPluginInfo; }
        }
        
        public override void Dispose()
        {

        }

        protected override void OnProcessEvents(List<MidiMessage> midiMessages)
        {

        }

        protected override void OnChangeBufferSize(int bufferSize)
        {
#if !Portable
            Effect_Reverb_Clear(mReverbPtr);
#endif
            mReverbCs.ClearReverb();
        }

        protected override void OnChangeSampleRate(double sampleRate)
        {
#if !Portable
            Effect_Reverb_Clear(mReverbPtr);
#endif
            mReverbCs.ClearReverb();
        }

        protected override void OnChangeInputChannels(int channels)
        {

        }

        protected override void OnChangeOutputChannels(int channels)
        {

        }
    }
}
